#include "Tools.h"

#include <stdarg.h>

/**
 * @brief  微秒级延时
 * @param  xus 延时时长，范围：0~233015
 * @retval 无
 */
void
delay_us(uint32_t xus)
{
        SysTick->LOAD = 72 * xus;   // 设置定时器重装值
        SysTick->VAL  = 0x00;       // 清空当前计数值
        SysTick->CTRL = 0x00000005; // 设置时钟源为HCLK，启动定时器
        while (!(SysTick->CTRL & 0x00010000))
                ;                   // 等待计数到0
        SysTick->CTRL = 0x00000004; // 关闭定时器
}

/**
 * @brief  毫秒级延时
 * @param  ms 延时时长，范围：0~4294967295
 * @retval 无
 */
void
delay_ms(uint32_t ms)
{
        while (ms--)
                delay_us(1000);
}

/**
 * @brief  秒级延时
 * @param  s 延时时长，范围：0~4294967295
 * @retval 无
 */
void
delay_s(uint32_t s)
{
        while (s--)
                delay_ms(1000);
}

bool
strcmp_b(const char *str1, const char *str2)
{
        while (1) {
                if (*str1 != *str2)
                        return false;
                if (!*str1)
                        break;
                ++str1;
                ++str2;
        }
        return true;
}

void
strcpy_b(char *dest, const char *src, const uint16_t size)
{
        for (int i = 0; i < size; ++i) {
                if (src[i] == '\0') {
                        dest[i] = '\0';
                        break;
                }
                dest[i] = src[i];
        }
}

uint16_t
strlen_b(const char *str)
{
        const char *str_cur = str;
        while (*str_cur++) {
        }
        return str_cur - str - 1;
}

/**
 * @brief 浮点特化的格式转换
 */
static void
float_format(char **dest, double num, const uint8_t reserve)
{
        uint8_t buffer[10] = {0};
        /* 先判断正负 */
        if (num < 0) {
                *(*dest)++ = '-';
                num        = -num;
        }
        /* 根据保留位数进行缩放 */
        if (reserve == 1)
                num *= 10.0;
        else if (reserve == 3)
                num *= 1000.0;
        else
                num *= 100.0;
        /* 转换为正整数，存入缓冲 */
        unsigned int real_num = (unsigned int)num;
        for (int i = 0; i < 10 && real_num; ++i) {
                buffer[i] = real_num % 10;
                real_num /= 10;
        }
        /* 转实际浮点数到目标缓冲 */
        bool have_real_num = false;
        for (int i = 9; i >= 0; --i) {
                if (i == reserve) {
                        have_real_num = true;
                        *(*dest)++    = buffer[i] + '0';
                        *(*dest)++    = '.';
                } else if (buffer[i] != 0 || have_real_num) {
                        *(*dest)++    = buffer[i] + '0';
                        have_real_num = true;
                }
        }
}

/**
 * @brief 整数特化的格式转换
 */
static void
integer_format(char       **dest,
               unsigned int num,
               const bool   is_unsigned,
               const bool   is_save_tm_fmt)
{
        unsigned char buffer[10] = {0};
        if (!is_unsigned && (int)num < 0) {
                *(*dest)++ = '-';
                //
                num = (unsigned int)-(int)num;
        }
        /* 转换为正整数，存入缓冲 */
        for (int i = 0; i < 10 && num; ++i) {
                buffer[i] = num % 10;
                num /= 10;
        }
        /* 转实际整数到目标缓冲 */
        bool have_real_num = false;
        for (int i = 9; i >= 0; --i) {
                if (buffer[i] != 0 || have_real_num) {
                        *(*dest)++    = buffer[i] + '0';
                        have_real_num = true;
                }
                if (i == 1 && is_save_tm_fmt && !have_real_num)
                        *(*dest)++ = '0';
        }
        if (!have_real_num)
                *(*dest)++ = '0';
}

void
sprintf_b(char *dest, const char *format, ...)
{
        const char *p = format;
        va_list     args;
        /* 浮点保留位数 */
        uint8_t float_reserve = 2;
        /* 是否格式保留时间的 0，如 00:02 */
        bool is_save_tm_fmt = false;
        /**
         * @brief
         * 状态机，0 起始、1 格式化、2 字符输入、3 出错、
         *         4 结束、5 浮点位数特化、
         */
        uint8_t state = 0;

        va_start(args, format);

re_parser:
        switch (state) {
        case 0:
                if (*p == '%') {
                        state = 1;
                } else if (*p == '\0') {
                        state = 4;
                } else {
                        state   = 2;
                        *dest++ = *p;
                }
                p += 1;
                break;
        case 1:
                if (*p == 'f') {
                        float_format(&dest,
                                     va_arg(args, double),
                                     float_reserve);
                } else if (*p == '.') {
                        state = 5;
                        p += 1;
                        break;
                } else if (*p == 'c') {
                        *dest++ = (unsigned char)va_arg(args, unsigned int);
                } else if (*p == 'd') {
                        integer_format(&dest,
                                       va_arg(args, int),
                                       false,
                                       is_save_tm_fmt);
                } else if (*p == 'u') {
                        integer_format(&dest,
                                       va_arg(args, unsigned int),
                                       true,
                                       is_save_tm_fmt);
                } else if (*p == 's') {
                        const char *_sp = va_arg(args, const char *);
                        while (*_sp)
                                *dest++ = *_sp++;
                } else if (*p == '%') {
                        *dest++ = '%';
                } else if (*p == '2') {
                        is_save_tm_fmt = true;
                        p += 1;
                        break;
                } else {
                        state = 3;
                        break;
                }
                state = 2;
                p += 1;
                break;
        case 5:
                state = 3;
                if (*p == '1')
                        float_reserve = 1;
                else if (*p == '2')
                        float_reserve = 2;
                else if (*p == '3')
                        float_reserve = 3;
                else
                        break;
                state = 1;
                p += 1;
                break;
        case 2:
                if (*p == '%')
                        state = 1;
                else if (*p == '\0')
                        state = 4;
                else
                        *dest++ = *p;
                p += 1;
                break;
        case 3:
                *dest++ = 'E';
                *dest++ = 'R';
                *dest++ = 'R';
                state     = 4;
                break;
        default:
                *dest = '\0';
                va_end(args);
                return;
        }
        goto re_parser;
}
